package cn.tangshh.universal.core.util;

import cn.hutool.core.util.StrUtil;
import cn.tangshh.universal.core.exception.FunctionException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.lang.reflect.Method;

/**
 * SpEL工具类
 *
 * @author Tang
 * @version v1.0
 * @since 2024/05/30
 */
@Slf4j
public final class SpELUtl {
    private final static ExpressionParser PARSER = new SpelExpressionParser();
    private final static ParameterNameDiscoverer DISCOVERER = new DefaultParameterNameDiscoverer();

    private SpELUtl() {
    }

    /**
     * 表达式解析
     *
     * @param expression 表达式
     * @param context    上下文
     * @param tClass     T级
     * @return {@link T }
     */
    public static <T> T parse(String expression, EvaluationContext context, Class<T> tClass) {
        if (StrUtil.isBlank(expression)) {
            return null;
        }
        return PARSER.parseExpression(expression).getValue(context, tClass);
    }

    /**
     * 处理上下文
     *
     * @param joinPoint 切入点
     * @return {@link EvaluationContext }
     */
    public static EvaluationContext handleContext(JoinPoint joinPoint) {
        if (joinPoint.getSignature() instanceof MethodSignature) {
            Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
            return handleContext(joinPoint, method);
        }
        throw new FunctionException("无法处理方法以外的上下文");
    }

    /**
     * 处理上下文
     *
     * @param joinPoint 切入点
     * @param method    方法
     * @return {@link EvaluationContext }
     */
    public static EvaluationContext handleContext(JoinPoint joinPoint, Method method) {
        StandardEvaluationContext context = new StandardEvaluationContext();
        String[] names = DISCOVERER.getParameterNames(method);
        Object[] args = joinPoint.getArgs();
        if (names != null) {
            for (int i = 0; i < names.length; i++) {
                context.setVariable(names[i], args[i]);
            }
        }
        context.setVariable("method", method);
        return context;
    }
}
